<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>carousel</title>
</head>
<style>
    .carousel {
        width: 500px;
        height: 300px;
        overflow: hidden;
        position: relative;
        text-align: center;
    }

    .carousel img {
        width: 500px;
        height: 300px;
        opacity: 0;
        transition: opacity 0.3s;
    }


    .left, .right {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        font-size: 50px;
        opacity: 0.6;
        cursor: pointer;
    }

    .left {
        left: 0;
    }

    .right {
        right: 0;
    }

    ul.dots {
        list-style-type: none;
        padding: 0;
        margin: 0;
        position: absolute;
        bottom: 24px;
        left: 50%;
        transform: translateX(-50%);
    }

    .dots li {
        width: 10px;
        height: 10px;
        border: 2px solid #fff;
        border-radius: 100%;
        float: left;
        margin: 4px;
        cursor: pointer;
    }

    .dots li:hover {
        background-color: #51ccff;
        /*你也可以显示得更加鲜明*/
    }

    .dots li.active {
        background-color: #333333;
    }


    .carousel .appear {
        opacity: 1;
        position: absolute;
        left: 0;
        top: 0;
    }


</style>
<body>
<div class="carousel">
    <span>
        <img class="appear" src="./images/img1.jpg" alt="img1">
        <img src="./images/img2.jpeg" alt="img2">
        <img src="./images/img3.jpg" alt="img3">
        <img src="./images/img4.jpg" alt="img4">
        <img src="./images/img5.jpg" alt="img5">
    </span>
    <div class="left"><</div>
    <div class="right">></div>
    <ul class="dots">
    </ul>
</div>
严格来说这种切换动画时一种更好的选择,无论对于是代码的优化,还是代码的量,都是如此
<script type="text/javascript">
    const carousel = document.querySelector('.carousel')
    const album = carousel.querySelector('span')
    const images = carousel.querySelectorAll('img')
    const imagesNumber = album.children.length
    const toLeftElement = carousel.querySelector('.left')
    const toRightElement = carousel.querySelector('.right')
    const dots = carousel.querySelector('.dots')

    console.log(carousel, album, images, toLeftElement, toRightElement)
    // 这边可以加上判断 若没有这些 dom 则停止运行
    // 在 vue/react 中则不同

    let timeGap = 3 * 1000 // 每张图的显示时间
    let position = 0;
    let start = 0;
    let rafId;
    let timeoutId;

    function renderDots() {
        let string = ''
        for (let i = 0; i < imagesNumber; i++) {
            if (position === i) {
                string += `<li class="active"  data-p="${i}"></li>`
            } else {
                string += `<li data-p="${i}"></li>`
            }
        }
        dots.innerHTML = string
    }

    renderDots()

    // 只需要第一次渲染 html 就足够了
    // 后续只需要渲染对应于位置的 class 即可

    function renderDotsPosition() {
        let dotsChildren = dots.children,
            dotsLength = dotsChildren.length
        while (dotsLength--) {
            const dot = dotsChildren[dotsLength]
            dot.classList.toggle("active", position === dotsLength);
        }
    }

    function renderNext() {
        let nextPosition = position + 1
        if (nextPosition >= imagesNumber) {
            nextPosition = 0
        }
        render(position, nextPosition)
    }

    function renderPrev() {
        let nextPosition = position - 1
        if (nextPosition < 0) {
            nextPosition = imagesNumber - 1
        }
        render(position, nextPosition)
    }

    function render(cPosition, nPosition) {
        const curImage = images[cPosition]
        const nextImage = images[nPosition]
        curImage.classList.remove('appear')
        nextImage.classList.add('appear')
        position = nPosition
        renderDotsPosition()
    }

    function step(timestamp) {
        // console.log(timestamp)
        let progress = timestamp - start
        if (progress >= timeGap) {
            renderNext()
            console.log(position)
            start = timestamp;
        }
        rafId = window.requestAnimationFrame(step);
    }

    rafId = window.requestAnimationFrame(step);

    function slightPauseRAF() {
        cancelAnimationFrame(rafId)
        if (timeoutId) {
            clearTimeout(timeoutId)
            timeoutId = 0
        }
        timeoutId = setTimeout(() => {
            rafId = window.requestAnimationFrame(step);
        }, timeGap - 300)
    }

    function leftRightClickHandle(ev) {
        slightPauseRAF()
        const target = ev.target
        target.classList.contains('right') ?
            renderNext()
            : renderPrev()
    }

    toRightElement.addEventListener('click', leftRightClickHandle, false)
    toLeftElement.addEventListener('click', leftRightClickHandle, false)

    dots.addEventListener('click', function (ev) {
        const target = ev.target
        if (target.nodeName === 'UL' || target.classList.contains('active')) {
            return false;
        }
        // 当点击了 ul 或者 当前元素时正显示的 dot 时 return
        slightPauseRAF()
        const p = target.dataset.p
        render(position, p | 0)
    })

</script>
</body>
</html>
